﻿Ext.data.Store = Ext.extend(Ext.data.AbstractStore, {

    //如果为true，则把排序操作推迟到服务端执行。如果为false，在本地客户端进行排序。默认为false。
    remoteSort: false,

    //如果为true，则把过滤操作推迟到服务端执行。如果为false，在本地客户端进行过滤。默认为false。
    remoteFilter: false,

    //（可选的），store的数据根据groupField字段进行分组
    groupField: undefined,

    //分组时采用的排序的方向。默认为“ASC” - 另一个值是“DESC”
    groupDir: "ASC",

    //每页的记录数，这是用来实现内置的翻页功能。默为25。
    pageSize: 25,

    //store最近加载的页面（见loadPage）
    currentPage: 1,

    //如果设为true，则通过调用loadPage，nextPage或previousPage（默认为true）类加载另一个页面时，清空store。
    //设置为false将保持现有的记录，允许一次加载大数据集。
    clearOnPageLoad: true,


    implicitModel: false,


    loading: false,

    //仅适用于本地过滤，过滤后调用排序方法，默认为true
    sortOnFilter: true,

    isStore: true,


    constructor: function (config) {
        config = config || {};

        //Store的数据集合
        this.data = new Ext.util.MixedCollection(false, function (record) {
            return record.internalId;//id获取规则
        });

        //捕获参数传来的数据
        if (config.data) {
            this.inlineData = config.data;
            delete config.data;
        }

        Ext.data.Store.superclass.constructor.call(this, config);

        var proxy = this.proxy,
            data = this.inlineData;

        //如果初始化时有数据
        if (data) {
            if (proxy instanceof Ext.data.MemoryProxy) {//如果proxy是一个Ext.data.MemoryProxy实例
                proxy.data = data;
                this.read();//读取数据
            } else {
                this.add.apply(this, data);//向this.data中添加数据
            }

            this.sort();//对this.data中的数据进行分组排序
            delete this.inlineData;
        } 
        //如果在store对象创建后自动运行load方法
        else if (this.autoLoad) {
            //延时10毫秒后调用load方法，如果this.autoLoad是对象，则作为方法的参数
            Ext.defer(this.load, 10, this, [typeof this.autoLoad == 'object' ? this.autoLoad : undefined]);
        }
    },


    getGroups: function () {
        var records = this.data.items,
            length = records.length,
            groups = [],
            pointers = {},
            record, groupStr, group, i;

        for (i = 0; i < length; i++) {
            record = records[i];
            groupStr = this.getGroupString(record);
            group = pointers[groupStr];

            if (group == undefined) {
                group = {
                    name: groupStr,
                    children: []
                };

                groups.push(group);
                pointers[groupStr] = group;
            }

            group.children.push(record);
        }

        return groups;
    },


    getGroupString: function (instance) {
        return instance.get(this.groupField);
    },

    //获取第一条记录
    first: function () {
        return this.data.first();
    },

    //获取最后一条记录
    last: function () {
        return this.data.last();
    },

    //往this.data中加新的记录
    insert: function (index, records) {
        var i, record, len;

        records = [].concat(records);
        for (i = 0, len = records.length; i < len; i++) {
            record = this.createModel(records[i]);//创建一个model实例
            record.set(this.modelDefaults);

            this.data.insert(index + i, record);//加入新记录
            record.join(this);//通知model实例，该实例已经被加入改store中
        }

        //this.snapshot：store中的原始（未筛选）集合。这是用来在过滤器被移除或改变时恢复记录的
        if (this.snapshot) {
            this.snapshot.addAll(records);
        }

        this.fireEvent('add', this, records, index);
        this.fireEvent('datachanged', this);
    },

    //向Store添加model实例，传入的是model的配置对象
    add: function (records) {

        if (!Ext.isArray(records)) {//将records转换成数组
            records = Array.prototype.slice.apply(arguments);
        }

        var length = records.length,
            record, i;

        for (i = 0; i < length; i++) {
            record = this.createModel(records[i]);//创建model实例

            if (record.phantom == false) {
                record.needsAdd = true;
            }

            records[i] = record;
        }

        //往this.data中加新的记录
        this.insert(this.data.length, records);

        return records;
    },

    //创建model实例
    createModel: function (record) {
        if (!(record instanceof Ext.data.Model)) {
            record = Ext.ModelMgr.create(record, this.model);
        }

        return record;
    },

    //遍历每条记录，没遍历到一条记录运行一次fn函数
    each: function (fn, scope) {
        this.data.each(fn, scope);
    },

    //从store中一次指定的记录，每移除一条记录触发一次'remove'事件，所有记录都移除完毕触发'datachanged'事件
    remove: function (records) {
        if (!Ext.isArray(records)) {
            records = [records];
        }

        var length = records.length,
            i, index, record;

        for (i = 0; i < length; i++) {
            record = records[i];
            index = this.data.indexOf(record);

            if (index > -1) {
                this.removed.push(record);
                //this.snapshot：store中的原始（未筛选）集合。这是用来在过滤器被移除或改变时恢复记录的
                if (this.snapshot) {
                    this.snapshot.remove(record);
                }

                record.unjoin(this);
                this.data.remove(record);

                this.fireEvent('remove', this, record, index);
            }
        }

        this.fireEvent('datachanged', this);
    },


    removeAt: function (index) {
        var record = this.getAt(index);

        if (record) {
            this.remove(record);
        }
    },

    //通过配置的代理类将数据加载到当前store中。
    //使用代理服务器进行异步调用任何存储后端代理使用
    //如果有必要，则自动向当前Store添加检索类的实例，并调用回调函数
    load: function (options) {
        options = options || {};

        if (Ext.isFunction(options)) {//如果传进来的参数是一个函数
            options = {
                callback: options//则把该函数做为回调函数
            };
        }

        Ext.applyIf(options, {
            
            //groupField:（可选的），store的数据根据groupField字段进行分组 ,
            //groupDir:分组时采用的排序的方向。默认为“ASC” - 另一个值是“DESC”
            group: { field: this.groupField, direction: this.groupDir },
            start: 0,
            limit: this.pageSize, //每页的记录数
            addRecords: false
        });

        return Ext.data.Store.superclass.load.call(this, options);
    },


    isLoading: function () {
        return this.loading;
    },


    onProxyLoad: function (operation) {
        var records = operation.getRecords();

        this.loadRecords(records, operation.addRecords);
        this.loading = false;
        this.fireEvent('load', this, records, operation.wasSuccessful());



        this.fireEvent('read', this, records, operation.wasSuccessful());


        var callback = operation.callback;

        if (typeof callback == 'function') {
            callback.call(operation.scope || this, records, operation, operation.wasSuccessful());
        }
    },


    onProxyWrite: function (operation) {
        var data = this.data,
            action = operation.action,
            records = operation.getRecords(),
            length = records.length,
            callback = operation.callback,
            record, i;

        if (operation.wasSuccessful()) {
            if (action == 'create' || action == 'update') {
                for (i = 0; i < length; i++) {
                    record = records[i];

                    record.phantom = false;
                    record.join(this);
                    data.replace(record);
                }
            }

            else if (action == 'destroy') {
                for (i = 0; i < length; i++) {
                    record = records[i];

                    record.unjoin(this);
                    data.remove(record);
                }

                this.removed = [];
            }

            this.fireEvent('datachanged');
        }


        if (typeof callback == 'function') {
            callback.call(operation.scope || this, records, operation, operation.wasSuccessful());
        }
    },


    getNewRecords: function () {
        return this.data.filterBy(this.filterNew).items;
    },


    getUpdatedRecords: function () {
        return this.data.filterBy(this.filterDirty).items;
    },

    //对数据进行排序
    sort: function (sorters, direction) {
        //如果参数filters是字符串，则构造Ext.util.Sorter配置对象
        if (Ext.isString(sorters)) {
            var property = sorters,
                //存储每个字段当前的排序方向（“ASC”或“DESC”）。用于在内部管理每个字段排序方向的切换。只读
                sortToggle = this.sortToggle,
                toggle = Ext.util.Format.toggle;

            if (direction == undefined) {//如果没有定义排序方向
                //切换两个交替值之间的字符串。即当前是"ASC"，则切换到"DESC"，反之亦然
                sortToggle[property] = toggle(sortToggle[property] || "", "ASC", "DESC"); 
                direction = sortToggle[property];
            }

            sorters = {//赋予属性和排序方向
                property: property,
                direction: direction
            };
        }

        //如果有新的排序对象，则将原来的清空
        if (arguments.length != 0) {
            this.sorters.clear();
        }

        //添加新的排序对象
        this.sorters.addAll(this.decodeSorters(sorters));

        if (this.remoteSort) {//如果设置了在远程服务器中排序
            //获取远程数据
            this.load();
        } else {
            this.data.sort(this.sorters.items);//调用集合的排序功能

            this.fireEvent('datachanged', this);
        }
    },


    //过滤记录
    filter: function (filters, value) {

        //如果参数filters是字符串，则构造Ext.util.Filter配置对象
        if (Ext.isString(filters)) {
            filters = {
                property: filters,
                value: value
            };
        }

        //创建Ext.util.Filter对象并放入this.filters集合中
        this.filters.addAll(this.decodeFilters(filters));

        if (this.remoteFilter) {//如果是在远程服务器上过滤数据
            //调用服务加载数据
            this.load();
        } 
        //否则是在本地过滤数据
        else {

            //this.snapshot：store中的原始（未筛选）集合。这是用来在过滤器被移除或改变时恢复记录的
            this.snapshot = this.snapshot || this.data.clone();

            this.data = this.data.filter(this.filters.items);//获取筛选后的数据

            if (this.sortOnFilter && !this.remoteSort) {//如果设置了筛选后排序功能，且没设置远程排序
                this.sort();//排序数据
            } else {
                this.fireEvent('datachanged', this);
            }
        }
    },

    //清除过滤器，恢复被过滤的记录
    clearFilter: function (suppressEvent) {
        this.filters.clear();

        //如果有记录被过滤
        if (this.isFiltered()) {
            //this.snapshot：store中的原始（未筛选）集合。这是用来在过滤器被移除或改变时恢复记录的
            this.data = this.snapshot.clone();
            delete this.snapshot;

            if (suppressEvent !== true) {
                this.fireEvent('datachanged', this);
            }
        }
    },

    //是否有记录被过滤
    isFiltered: function () {
        //this.snapshot：store中的原始（未筛选）集合。这是用来在过滤器被移除或改变时恢复记录的
        return !!this.snapshot && this.snapshot != this.data;
    },

    //过滤记录
    //根据fn函数过滤数据，只有当fn函数返回不为false时数据才通过过滤器
    //fn接受的参数为记录和记录的键
    filterBy: function (fn, scope) {
        //this.snapshot：store中的原始（未筛选）集合。这是用来在过滤器被移除或改变时恢复记录的
        this.snapshot = this.snapshot || this.data.clone();//过滤记录前克隆完整记录
        this.data = this.queryBy(fn, scope || this);//数据改变
        this.fireEvent('datachanged', this);
    },

    //查询符合查询条件的记录
    //根据fn函数过滤数据，只有当fn函数返回不为false时数据才通过过滤器
    //fn接受的参数为记录和记录的键
    queryBy: function (fn, scope) {
        //this.snapshot：store中的原始（未筛选）集合。这是用来在过滤器被移除或改变时恢复记录的
        var data = this.snapshot || this.data;
        //调用集合的根据函数筛选数据的方法
        return data.filterBy(fn, scope || this);
    },

    //加载数据，data是要载入的数据，append是加载的形式，
    //为true表示以追加的形式加载，为false表示以覆盖的形式加载
    loadData: function (data, append) {
        var model = this.model,
            length = data.length,
            i, record;


        for (i = 0; i < length; i++) {
            record = data[i];

            //如果record不是一个Model对象
            if (!(record instanceof Ext.data.Model)) {
                data[i] = Ext.ModelMgr.create(record, model);//把record的数据封装成一个model对象
            }
        }
        //往store中加载一个model对象数组，触发'datachanged'事件。
        this.loadRecords(data, append);
    },

    //往store中加载一个model对象数组，触发'datachanged'事件。
    loadRecords: function (records, add) {
        if (!add) {
            this.data.clear();
        }

        this.data.addAll(records);


        for (var i = 0, length = records.length; i < length; i++) {
            records[i].needsAdd = false;
            records[i].join(this); //告诉这个model的实例，它已被添加到存储
        }

        //暂停触发的所有事件。
        this.suspendEvents();

        //remoteFilter：如果为true，则把过滤操作推迟到服务端执行。如果为false，在本地客户端进行过滤。默认为false。
        //filterOnLoad：如果为true，任何与该Store关联的过滤器将在数据加载之后运行，默认为true。remoteFilter如果为true，该属性将被忽略
        if (this.filterOnLoad && !this.remoteFilter) {
            this.filter(); //过滤记录
        }

        //remoteSort：如果为true，则把排序操作推迟到服务端执行。如果为false，在本地客户端进行排序。默认为false。
        //sortOnLoad：如果为true，任何与该Store关联的排序器将在数据加载之后运行，默认为true。remoteSort如果为true，该属性将被忽略
        if (this.sortOnLoad && !this.remoteSort) {
            this.sort();//排序记录
        }

        //继续触发事件。
        this.resumeEvents();
        this.fireEvent('datachanged', this, records);
    },


    //加载指定索引的页的数据
    loadPage: function (page) {
        this.currentPage = page;

        this.read({
            page: page,
            start: (page - 1) * this.pageSize,
            limit: this.pageSize,
            addRecords: !this.clearOnPageLoad//是否加载另一个页面时，清空store
        });
    },


    nextPage: function () {
        this.loadPage(this.currentPage + 1);
    },


    previousPage: function () {
        this.loadPage(this.currentPage - 1);
    },


    clearData: function () {
        this.data.each(function (record) {
            record.unjoin();
        });

        this.data.clear();
    },

    //返回指定字段的值满足特定值的记录的索引
    find: function (property, value, start, anyMatch, caseSensitive, exactMatch) {
        var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch); //创建筛选函数
        return fn ? this.data.findIndexBy(fn, null, start) : -1;//根据筛选函数返回记录索引
    },

    //返回指定字段的值满足特定值的记录
    findRecord: function () {
        var index = this.find.apply(this, arguments);
        return index != -1 ? this.getAt(index) : null;
    },

    //创建筛选函数
    createFilterFn: function (property, value, anyMatch, caseSensitive, exactMatch) {
        if (Ext.isEmpty(value)) {
            return false;
        }
        value = this.data.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
        return function (r) {
            return value.test(r.data[property]);
        };
    },

    //返回给定字段的值等于给定值的第一条记录的索引
    findExact: function (property, value, start) {
        return this.data.findIndexBy(function (rec) {
            return rec.get(property) === value;
        }, this, start);
    },

    //返回集合中满足条件的第一条记录的索引。函数fn返回true即为满足条件
    findBy: function (fn, scope, start) {
        return this.data.findIndexBy(fn, scope, start);
    },

    //返回store中特定字段的值的数组，且数组中的值不重复。
    //dataIndex代表字段名，allowNull代表是否允许有空值，bypassFilter代表是否一起取出被过滤掉的值
    collect: function (dataIndex, allowNull, bypassFilter) {
        var values = [],
            uniques = {},
            length, value, strValue, data, i;
        //this.snapshot：store中的原始（未筛选）集合。这是用来在过滤器被移除或改变时恢复记录的
        if (bypassFilter === true && this.snapshot) {
            data = this.snapshot.items;//连被过滤掉的值也一起取出
        } else {
            data = this.data.items;//取出过滤后的值
        }

        length = data.length;

        for (i = 0; i < length; i++) {
            value = data[i].data[dataIndex];//取出特定字段的值
            strValue = String(value);

            if ((allowNull || !Ext.isEmpty(value)) && !uniques[strValue]) {//如果允许有空值
                uniques[strValue] = true;//值的唯一性判断标识
                values[values.length] = value;
            }
        }

        return values;
    },


    sum: function (property, start, end) {
        var records = this.data.items,
            value = 0,
            i;

        start = start || 0;
        end = (end || end === 0) ? end : records.length - 1;

        for (i = start; i <= end; i++) {
            value += (records[i].data[property] || 0);
        }

        return value;
    },


    getCount: function () {
        return this.data.length || 0;
    },


    getAt: function (index) {
        return this.data.getAt(index);
    },


    getRange: function (start, end) {
        return this.data.getRange(start, end);
    },


    getById: function (id) {
        //this.snapshot：store中的原始（未筛选）集合。这是用来在过滤器被移除或改变时恢复记录的
        return (this.snapshot || this.data).findBy(function (record) {
            return record.getId() === id;
        });
    },


    indexOf: function (record) {
        return this.data.indexOf(record);
    },


    indexOfId: function (id) {
        return this.data.indexOfKey(id);
    },

    removeAll: function (silent) {
        var items = [];
        this.each(function (rec) {
            items.push(rec);
        });
        this.clearData();
        //this.snapshot：store中的原始（未筛选）集合。这是用来在过滤器被移除或改变时恢复记录的
        if (this.snapshot) {
            this.snapshot.clear();
        }



        if (silent !== true) {
            this.fireEvent('clear', this, items);
        }
    }
});